package com.zjny.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import com.zjny.constant.CommonConstant;
import com.zjny.constant.DateConstant;
import com.zjny.enums.FileTypeHexEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileItemFactory;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.mozilla.universalchardet.UniversalDetector;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * packageName com.zjny.util
 * 文件工具类
 * 1：通过File创建目录{@link FileUtils#mkDir(File)}
 * 2：通过路径创建目录{@link FileUtils#mkDir(String)}
 * 3：创建一个JSON格式的文件{@link FileUtils#createJsonFile(String, String, String)}
 * 4: 使用文件通道复制文件{@link FileUtils#copyFileWithChannel(File, File)}
 * 5: 计算文件的行数{@link FileUtils#countLines(File)}
 * 6: 快速清理大型文件{@link FileUtils#fastCleanFile(File)}
 * 7：将一个文件复制到目标路径{@link FileUtils#copy(File, String)}
 * 8：在指定目录及其子目录中搜索指定名称的文件{@link FileUtils#searchFile(File, String)}
 * 9：在指定目录及其子目录中搜索指定名称的文件{@link FileUtils#searchFileReg(File, String)}
 * 10：根据文件后缀名，从指定目录及其子目录中获取所有文件{@link FileUtils#getFilesBySuffix(File, String)}
 * 11：复制文件通过buffer{@link FileUtils#copyFileWithBuffer(File, File)}
 * 12：递归文件夹拿到所有的文件集合{@link FileUtils#getAllFiles(File, List)}
 * 13：递归文件夹拿到所有的文件集合{@link FileUtils#getAllFiles(File)}
 * 14：创建文件{@link FileUtils#createFile(String, String)}
 * 15: 拿到绝对路径{@link FileUtils#getAbsolutePath(File)}
 * 16：删除文件夹{@link FileUtils#deleteDir(File)}
 * 17：删除文件夹{@link FileUtils#deleteDir(String)}
 * 18：从字节数组拿到文件流{@link FileUtils#getInputStreamFromByteArray(byte[])}
 * 19：从字节数组拿到文件流{@link FileUtils#getInputStreamFromByteArray(byte[], int, int)}
 * 20: 从文件中拿到文件流{@link FileUtils#getInputStreamFromFile(File)}
 * 21：文件转成MultipartFile{@link FileUtils#fileToMultipartFile(File)}
 * 22: 文件转成MultipartFile{@link FileUtils#fileToMultipartFile(File, String)}
 * 23: 文件转成MultipartFile{@link FileUtils#fileToMultipartFile(File, String, String)}
 * 24: 文件转成MultipartFile{@link FileUtils#fileToMultipartFile(File, String, String, String)}
 * 25: 创建FileItem{@link FileUtils#createFileItem(File)}
 * 26: MultipartFile转File{@link FileUtils#multipartFileToFile(MultipartFile)}
 * 27: 获取文件的扩展名{@link FileUtils#getFileExt(File)}
 * 28: 获取文件的扩展名{@link FileUtils#getFileExt(String)}
 * 29: 获取文件的非扩展名部分{@link FileUtils#getFileNoExt(File)}
 * 30: 获取文件的非扩展名部分{@link FileUtils#getFileNoExt(String)}
 * 31：获取文件内容{@link FileUtils#getTxtContent(File, String)}
 * 32: 获取文件内容{@link FileUtils#getTxtContent(File)}
 * 33: 获取文件内容{@link FileUtils#getTxtContent(InputStream, String)}
 * 34: 获取文件编码格式{@link FileUtils#getFileEncode(File)}
 * 35: 获取文件编码格式{@link FileUtils#getFileEncode(String)}
 * 36: 获取文件编码格式{@link FileUtils#getFileEncode(InputStream)}
 * 37: 根据模值获取文件类型{@link FileUtils#getByMagicNumberCode(String)}
 * 38: 从指定文件中读取JSON内容，并将其转换为指定类型的Java对象{@link FileUtils#getJson(File, Class)}
 * 39: 赋值文件到指定的文件夹{@link FileUtils#copyFileToDir(String, String, String, boolean)}
 * 40: 重命名文件{@link FileUtils#renameFile(File, String)}
 * 41: 重命名并且拷贝文件{@link FileUtils#renameAndCopy(String, String, String, String)}
 * 42: 根据文件路径获取文件模值{@link FileUtils#getFileHeadHexByPath(String)}
 * 43: 输入流转成文件{@link FileUtils#inputStreamToFile(InputStream, String)}
 * 44：输入流转成文件{@link FileUtils#inputStreamToFile(InputStream, File)}
 * 45: 从URL中获取输入流{@link FileUtils#getInputStreamFromUrl(String)}
 * 46：文件上传{@link FileUtils#uploadFile(MultipartFile, String, String)}
 * 47: 通过文件拿到字节数组{@link FileUtils#getBytesByFiles(File)}
 * 48: 构建存储路径{@link FileUtils#pathBuild(String, String, String, boolean, boolean)}
 * 49: 文件下载{@link FileUtils#downloadFile(String, String)}
 * @author cui haida
 * @version JDK 8
 * 2025/6/18
 */

@Slf4j
public class FileUtils extends FileUtil {
    /**
     * 创建目录 -> by file
     *
     * @param file 文件
     * @return 是否成功
     */
    public static boolean mkDir(File file) {
        if (!file.exists()) {
            boolean mkFlag = file.mkdirs(); // 尝试创建目录
            if (!mkFlag) { // 创建目录失败
                log.error("创建目录失败: {}", file);
                return false;
            }
        }
        return true;
    }

    /**
     * 创建目录 -> by path
     *
     * @param path 路径
     * @return 是否成功
     */
    public static boolean mkDir(String path) {
        return mkDir(new File(path));
    }

    /**
     * 创建一个JSON格式的文件。
     *
     * @param jsonString 要写入文件的JSON字符串。
     * @param filePath   文件存储的路径。
     * @param fileName   文件名（不含扩展名）。
     * @return 返回创建的文件对象，如果创建失败则返回null。
     */
    public static File createJsonFile(String jsonString, String filePath, String fileName) {
        File file;
        try {
            // 拼接文件完整路径并生成json格式文件
            String fullPath = filePath + File.separator + fileName + ".json";

            // 确保文件所在的目录存在
            file = new File(fullPath);
            mkDir(file.getParentFile());
            deleteDir(file);

            // 创建新文件
            boolean flag = file.createNewFile();
            if (!flag) {
                log.error("创建文件失败: {}", file);
                return null;
            }

            // 对JSON字符串进行必要的转义处理
            if (jsonString.contains("'")) {
                jsonString = jsonString.replaceAll("'", "\\'");
            }
            if (jsonString.contains("\"")) {
                jsonString = jsonString.replaceAll("\"", "\\\"");
            }

            if (jsonString.contains("\r\n")) {
                jsonString = jsonString.replaceAll("\r\n", "\\u000d\\u000a");
            }
            if (jsonString.contains("\n")) {
                jsonString = jsonString.replaceAll("\n", "\\u000a");
            }

            // 将处理后的JSON字符串写入文件
            Writer write = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8);
            log.info("json文件内容：" + jsonString);
            write.write(jsonString);
            log.info("文件创建成功！");
            write.flush();
            write.close();
        } catch (IOException e) {
            log.error("创建json文件失败", e);
            return null;
        }
        return file;
    }


    /**
     * 使用文件通道复制文件。
     * 该方法通过文件输入流和文件输出流创建文件通道，然后使用通道的transferTo方法将源文件内容传输到目标文件。
     *
     * @param source 源文件，要复制的文件。
     * @param dest   目标文件，复制内容将存储在此文件中。
     */
    public static void copyFileWithChannel(File source, File dest) {
        try (
                FileInputStream inStream = new FileInputStream(source); // 创建文件输入流
                FileOutputStream outStream = new FileOutputStream(dest); // 创建文件输出流
                FileChannel in = inStream.getChannel(); // 通过文件输入流获取文件通道
                FileChannel out = outStream.getChannel(); // 通过文件输出流获取文件通道
        ) {
            in.transferTo(0, in.size(), out); // 将源文件通道的数据传输到目标文件通道
        } catch (IOException e) {
            log.error("复制文件失败", e);
        }
    }

    /**
     * 计算指定文件中的行数。
     *
     * @param file 指定要计算行数的文件。
     * @return 文件中的行数。如果发生异常，则返回0。
     */
    public static int countLines(File file) {
        try (LineNumberReader rf = new LineNumberReader(new FileReader(file))) {
            // 尝试通过跳过文件长度来快速计数行数
            long fileLength = file.length();
            long num = rf.skip(fileLength);
            // 如果跳过的字符数不等于文件长度，返回当前行号作为行数
            if (num != fileLength) {
                return rf.getLineNumber();
            }
            // 如果跳过的字符数等于文件长度，说明文件末尾已到达，返回当前行号作为行数
            return rf.getLineNumber();
        } catch (IOException e) {
            // 处理IO异常，并打印堆栈跟踪
            log.error("计算行数失败", e);
        }
        // 如果发生异常，则返回0
        return 0;
    }

    /**
     * 快速清空文件的内容。
     *
     * @param file 指定要清空内容的文件。
     * @return 若文件成功被清空，则返回true；若出现IO异常，则返回false。
     */
    public static boolean fastCleanFile(File file) {
        try (FileWriter fw = new FileWriter(file)) { // 尝试创建一个FileWriter实例来写入文件
            fw.write(CommonConstant.EMPTY); // 写入空字符或空字符串，以清空文件内容
            return true;
        } catch (IOException e) {
            e.printStackTrace(); // 捕获并打印IO异常
        }
        return false;
    }


    /**
     * 将一个文件复制到目标路径。
     *
     * @param file       源文件，不可为null。
     * @param targetFile 目标文件路径，不可为null。
     * @return 如果复制成功返回true，否则返回false。
     */
    public static boolean copy(File file, String targetFile) {
        try (
                FileInputStream fIn = new FileInputStream(file); // 创建文件输入流
                FileOutputStream fOut = new FileOutputStream(targetFile) // 创建文件输出流
        ) {
            FileChannel in = fIn.getChannel(); // 获取文件输入通道
            FileChannel out = fOut.getChannel(); // 获取文件输出通道
            // 设定缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(CommonConstant.BUFFER);
            while (in.read(buffer) != -1) { // 读取源文件数据到缓冲区
                // 准备写入，防止其他读取，锁住文件
                buffer.flip();
                out.write(buffer); // 将缓冲区的数据写入目标文件
                // 准备读取。将缓冲区清理完毕，移动文件内部指针
                buffer.clear();
            }
        } catch (IOException e) {
            log.error("复制文件失败", e);
        }
        return false; // 复制操作始终返回false，此处可能需要根据实际操作修改返回值
    }


    /**
     * 在指定目录及其子目录中搜索指定名称的文件。
     *
     * @param dirPath  指定的目录路径，为File类型。
     * @param fileName 需要搜索的文件名称。
     * @return 返回一个包含所有匹配文件名称的File对象的List列表。
     */
    public static List<File> searchFile(File dirPath, String fileName) {
        List<File> list = new ArrayList<>();
        File[] files = dirPath.listFiles();
        // 验证文件和目录是否存在
        if (valid(files)) {
            for (File file : files) {
                // 如果是目录，则递归搜索该目录
                if (file.isDirectory()) {
                    list.addAll(searchFile(file, fileName));
                } else {
                    // 如果是文件，则判断文件名是否匹配
                    String name = file.getName();
                    if (name.equals(fileName)) {
                        list.add(file);
                    }
                }
            }
        }
        return list;
    }


    /**
     * 在指定目录下搜索匹配给定正则表达式的文件。
     *
     * @param dirPath 指定的目录路径，为File类型。
     * @param reg     正则表达式，用于匹配文件名。
     * @return 返回一个包含所有匹配文件的List<File>。
     */
    public static List<File> searchFileReg(File dirPath, String reg) {
        // 初始化结果列表
        List<File> list = new ArrayList<>();
        File[] files = dirPath.listFiles();
        if (valid(files)) {
            for (File file : files) {
                if (file.isDirectory()) {
                    // 递归搜索目录中的文件
                    list.addAll(searchFile(file, reg));
                } else {
                    // 判断文件名是否与正则表达式匹配
                    String name = file.getName();
                    if (ReUtils.matchStatus(reg, name)) {
                        list.add(file);
                    }
                }
            }
        }
        return list;
    }

    /**
     * 根据文件后缀名，从指定目录及其子目录中获取所有文件。
     *
     * @param dir    指定的目录，将从此目录及其子目录中查找文件。
     * @param suffix 需要查找的文件后缀名（不区分大小写）。
     * @return 返回一个包含所有符合条件文件的列表。
     */
    public static List<File> getFilesBySuffix(File dir, String suffix) {
        List<File> list = new ArrayList<File>();
        File[] files = dir.listFiles();

        // 验证文件数组是否有效，避免空指针异常
        if (valid(files)) {
            for (File file : files) {
                // 如果是目录，则递归查找该目录下的文件
                if (file.isDirectory()) {
                    list.addAll(getFilesBySuffix(file, suffix));
                } else {
                    // 获取文件名并转为小写，以统一大小写处理
                    String fileName = file.getName().toLowerCase();
                    // 判断文件名是否以指定的后缀名结尾
                    if (fileName.endsWith(suffix.toLowerCase())) {
                        list.add(file);
                    }
                }
            }
        }
        return list;
    }

    /*
     * 验证文件数组是否有效。这里可以添加具体的验证逻辑。
     * 例如，判断files是否为null或长度是否大于0等。
     */
    private static boolean valid(File[] files) {
        // 这里添加验证逻辑
        return files != null && files.length > 0;
    }

    /**
     * 验证对象是否为空。
     *
     * @param obj 要验证的对象。
     * @return 如果对象不为空，则返回true；否则返回false。
     */
    private static boolean valid(Object obj) {
        return !(null == obj);
    }

    /**
     * 使用缓冲区复制文件。
     * 从源文件到目标文件的字节内容将被复制。
     *
     * @param source 源文件，不可为null。
     * @param target 目标文件，不可为null。
     */
    public static void copyFileWithBuffer(File source, File target) {
        try (
                FileInputStream inStream = new FileInputStream(source); // 创建源文件的输入流
                FileOutputStream outStream = new FileOutputStream(target); // 创建目标文件的输出流
                FileChannel in = inStream.getChannel(); // 获取源文件的文件通道
                FileChannel out = outStream.getChannel() // 获取目标文件的文件通道
        ) {
            ByteBuffer buffer = ByteBuffer.allocate(4096); // 分配一个4KB的缓冲区
            while (in.read(buffer) != -1) { // 循环读取源文件直到末尾
                buffer.flip(); // 将缓冲区切换为写模式
                out.write(buffer); // 将缓冲区的内容写入目标文件
                buffer.clear(); // 清空缓冲区以供下一次使用
            }
        } catch (IOException e) {
            e.printStackTrace(); // 处理IO异常
        }
    }

    /**
     * 递归文件夹拿到所有的文件集合
     *
     * @param file     文件
     * @param fileList 文件集合
     */
    public static void getAllFiles(File file, List<File> fileList) {
        if (file == null || !file.exists()) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    getAllFiles(f, fileList);
                }
            }
        } else {
            fileList.add(file);
        }
    }


    /**
     * 读取doc文件的内容
     *
     * @param filePath 文件路径
     * @return 文件文件内容
     */
    public static String readDocFile(String filePath) {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            HWPFDocument hwpfDocument = new HWPFDocument(fis);
            StringBuilder text = hwpfDocument.getText();
            return text.toString();
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 读取docx的文件的内容
     *
     * @param filePath 文件路径
     * @return 文件内容
     */
    public static String readDocxFile(String filePath) {
        // 读取docx文件
        StringBuilder sbd = new StringBuilder();
        try (NiceXWPFDocument docx = XWPFTemplate.compile(filePath).getXWPFDocument();) {
            List<XWPFParagraph> paragraphs = docx.getParagraphs();
            for (XWPFParagraph paragraph : paragraphs) {
                sbd.append(paragraph.getText()).append("\n");
            }
        } catch (Exception e) {
            log.warn("读取docx文件失败！路径：【{}】异常信息：", filePath, e);
        }
        return sbd.toString();
    }

    /**
     * 获取指定目录及其子目录中的所有文件列表。
     *
     * @param srcFile 指定的源文件或目录。
     * @return 所有文件的列表，包括源目录下的文件和所有子目录中的文件。
     */
    public static List<File> getAllFiles(File srcFile) {
        List<File> fileList = new ArrayList<>();
        File[] tmp = srcFile.listFiles();
        if (tmp == null) {
            return fileList;
        }
        for (File file : tmp) {
            // 添加文件到列表
            if (file.isFile()) {
                fileList.add(file);
            }
            // 递归处理目录
            if (file.isDirectory()) {
                // 若不是空目录，则递归添加其下的文件和目录
                if (file.listFiles() != null && Objects.requireNonNull(file.listFiles()).length != 0) {
                    fileList.addAll(getAllFiles(file));
                } else {
                    // 若是空目录，则直接添加到列表
                    fileList.add(file);
                }
            }
        }
        return fileList;
    }

    /**
     * 获取指定目录及其子目录中的所有文件列表。
     *
     * @param srcFile 指定的源文件或目录。
     * @return 所有文件的列表，包括源目录下的文件和所有子目录中的文件。
     */
    public static List<File> getAllFilesFilter(File srcFile, List<String> suffixList) {
        List<File> fileList = new ArrayList<>();
        File[] tmp = srcFile.listFiles();
        if (tmp == null) {
            return fileList;
        }
        for (File file : tmp) {
            // 添加文件到列表
            if (file.isFile()) {
                if (CollUtils.contains(suffixList, getFileExt(file))) {
                    fileList.add(file);
                }
            }
            // 递归处理目录
            if (file.isDirectory()) {
                // 若不是空目录，则递归添加其下的文件和目录
                if (file.listFiles() != null && Objects.requireNonNull(file.listFiles()).length != 0) {
                    fileList.addAll(getAllFilesFilter(file, suffixList));
                }
            }
        }
        return fileList;
    }


    /**
     * 创建文件
     * 根据压缩包内文件名和解压缩目的路径，创建解压缩目标文件，
     * 生成中间目录
     *
     * @param dstPath  解压缩目的路径
     * @param fileName 压缩包内文件名
     * @return 解压缩目标文件
     */
    public static File createFile(String dstPath, String fileName) {
        //将文件名的各级目录分解
        String[] dirs = fileName.split("/");
        File file = new File(dstPath);
        if (dirs.length > 1) {
            //文件有上级目录
            for (int i = 0; i < dirs.length - 1; i++) {
                //依次创建文件对象知道文件的上一级目录
                file = new File(file, dirs[i]);
            }
            mkDir(file);
            //创建文件
            file = new File(file, dirs[dirs.length - 1]);
        } else {
            mkDir(file);
            //创建文件
            file = new File(file, dirs[0]);
        }
        return file;
    }

    /**
     * 获取指定文件相对于指定目录的路径。
     *
     * @param dirPath 指定的目录路径，为字符串形式。
     * @param file    指定的文件，为File类的实例。
     * @return 返回一个字符串，表示文件相对于目录的相对路径。
     */
    public static String getRelativePath(String dirPath, File file) {
        // 将目录路径转换为File对象
        File dir = new File(dirPath);
        // 初始化用于构建相对路径的StringBuilder，以文件名开始
        StringBuilder relativePath = new StringBuilder(file.getName());
        while (true) {
            // 逐级向上获取文件的父目录
            file = file.getParentFile();
            if (file == null) {
                // 如果文件的父目录为空，到达根目录，退出循环
                break;
            }
            if (file.equals(dir)) {
                // 如果文件的父目录等于指定的目录，说明已经找到相对路径，退出循环
                break;
            } else {
                // 将文件的父目录名称添加到相对路径的前面
                relativePath.insert(0, file.getName() + "/");
            }
        }    // end while

        // 返回构建好的相对路径字符串
        return relativePath.toString();
    }


    /**
     * 删除指定目录文件夹及下面所有文件
     *
     * @param file 文件
     */
    public static void deleteDir(File file) {
        if (!file.exists()) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    deleteDir(f);
                }
            }
        } else {
            boolean deleteFlag = file.delete();
            if (!deleteFlag) {
                log.error("删除文件失败: {}", file);
            }
        }
    }

    /**
     * 删除指定目录文件夹及下面所有文件
     *
     * @param path 路径
     */
    public static void deleteDir(String path) {
        deleteDir(new File(path));
    }

    /**
     * byte[] -> inputStream
     *
     * @param bytes byte[]
     * @return 流
     */
    public static InputStream getInputStreamFromByteArray(byte[] bytes) {
        return new ByteArrayInputStream(bytes);
    }

    /**
     * byte[] -> inputStream
     *
     * @param bytes  byte[]
     * @param offset 偏移大小
     * @param length 长度
     * @return 流
     */
    public static InputStream getInputStreamFromByteArray(byte[] bytes, int offset, int length) {
        return new ByteArrayInputStream(bytes, offset, length);
    }

    /**
     * file -> inputStream
     *
     * @param file 文件
     * @return 流
     * @throws IOException io异常
     */
    public static InputStream getInputStreamFromFile(File file) throws IOException {
        return Files.newInputStream(file.toPath());
    }

    /**
     * 将File对象转换为MultipartFile对象
     *
     * @param file 需要转换的文件
     * @return 转换后的MultipartFile对象
     * @throws IOException 如果读取文件时发生错误
     */
    public static MultipartFile fileToMultipartFile(File file) throws IOException {
        return fileToMultipartFile(file, null, null, null);
    }

    /**
     * 将File对象转换为MultipartFile对象
     *
     * @param file     需要转换的文件
     * @param fileName 指定的文件名，如果不提供则使用文件本身的名称
     * @return 转换后的MultipartFile对象
     * @throws IOException 如果读取文件时发生错误
     */
    public static MultipartFile fileToMultipartFile(File file, String fileName) throws IOException {
        return fileToMultipartFile(file, fileName, null, null);
    }

    /**
     * 将File对象转换为MultipartFile对象
     *
     * @param file        需要转换的文件
     * @param fileName    指定的文件名，如果不提供则使用文件本身的名称
     * @param contentType 指定的内容类型，如果不提供则使用文件的MIME类型
     * @return 转换后的MultipartFile对象
     * @throws IOException 如果读取文件时发生错误
     */
    public static MultipartFile fileToMultipartFile(File file, String fileName, String contentType) throws IOException {
        return fileToMultipartFile(file, fileName, contentType, null);
    }

    /**
     * 将File对象转换为MultipartFile对象
     *
     * @param file        需要转换的文件
     * @param fileName    指定的文件名，如果不提供则使用文件本身的名称
     * @param contentType 指定的内容类型，如果不提供则使用文件的MIME类型
     * @param name        指定上传文件的名称
     * @return 转换后的MultipartFile对象
     * @throws IOException 如果读取文件时发生错误
     */
    public static MultipartFile fileToMultipartFile(File file, String fileName, String contentType, String name) throws IOException {
        // 后面几个参数没有的情况下使用默认值
        if (fileName == null || fileName.isEmpty()) {
            fileName = file.getName(); // 使用文件本身的名称
        }
        contentType = StringUtils.isBlank(contentType) ? getContentType(file) : contentType;
        name = StringUtils.isBlank(name) ? "file" : name;

        // 进行转换
        InputStream fileInputStream = getInputStreamFromFile(file);
        MultipartFile multipartFile = new MockMultipartFile(name, fileName, contentType, fileInputStream);
        fileInputStream.close(); // 关闭输入流，释放资源

        return multipartFile;
    }

    /**
     * 创建一个FileItem实例，用来表示文件项。
     *
     * @param file 需要被上传的文件。
     * @return FileItem 实例，包含了文件的内容和元数据。
     */
    public static FileItem createFileItem(File file) {
        // 创建一个DiskFileItemFactory实例，用于配置FileItem的存储方式
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        // 使用工厂方法创建一个FileItem实例，指定字段名、内容类型、是否允许表单字段有文件名，以及文件名
        FileItem item = factory.createItem("courtTranscriptFile", "text/plain", true, file.getName());
        int bytesRead;
        byte[] buffer = new byte[8192];
        try {
            FileInputStream fis = new FileInputStream(file);
            OutputStream os = item.getOutputStream();
            // 从文件读取数据并写入到FileItem中
            while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            fis.close();
        } catch (IOException e) {
            log.error(e.getMessage()); // 记录文件读写过程中的IO异常
        }
        return item;
    }


    /**
     * 将MultipartFile类型的文件转换为File类型。
     * 这个方法会尝试将上传的文件保存到一个以文件名命名的新文件中。
     * 如果文件名不存在，将使用默认名称"temp"。
     * 如果无法创建目标文件所在的目录，将返回null。
     *
     * @param file 上传的文件对象，不可为null。
     * @return 转换后的File对象，如果转换失败或文件为空，则返回null。
     */
    public static File multipartFileToFile(MultipartFile file) {
        // 检查文件是否为空
        if (file == null || file.isEmpty()) {
            return null;
        }
        // 获取文件原名，若为空则使用默认名称
        String fileName = file.getOriginalFilename() == null ? "temp" : file.getOriginalFilename();
        File targetFile = new File(fileName);
        // 检查文件目录是否存在，不存在则尝试创建
        boolean mkdirFlag = mkDir(targetFile);
        if (!mkdirFlag) {
            return null;
        }
        try {
            FileOutputStream out = new FileOutputStream(targetFile); // 创建文件输出流
            out.write(file.getBytes()); // 写入文件数据
            out.flush(); // 刷新输出流
            out.close(); // 关闭输出流
        } catch (Exception e) {
            log.error("文件写入失败");
        }
        return targetFile; // 返回创建的File对象
    }

    /**
     * 获取文件类型
     *
     * @param file 文件
     * @return 文件类型
     */
    private static String getContentType(File file) {
        return FileUtils.getMimeType(file.getName());
    }

    /**
     * 获取文件扩展名
     *
     * @param fileName 文件名称
     * @return 文件扩展名
     */
    public static String getFileExt(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return CommonConstant.EMPTY;
        }
        int index = fileName.lastIndexOf(".");
        if (index == -1) {
            return CommonConstant.EMPTY;
        }
        return fileName.substring(index + 1);
    }

    /**
     * 获取文件扩展名
     *
     * @param file 文件
     * @return 文件扩展名
     */
    public static String getFileExt(File file) {
        return getFileExt(file.getName());
    }

    /**
     * 从文件名中获取不包含扩展名的部分。
     *
     * @param file 文件
     * @return 如果文件名不为空且不为null，则返回不包含扩展名的文件名；
     * 如果文件名为空、null或不包含扩展名，则返回空字符串。
     */
    public static String getFileNoExt(File file) {
        return getFileNoExt(file.getName());
    }

    /**
     * 从文件名中获取不包含扩展名的部分。
     *
     * @param fileName 文件名，可以为空或null。
     * @return 如果文件名不为空且不为null，则返回不包含扩展名的文件名；
     * 如果文件名为空、null或不包含扩展名，则返回空字符串。
     */
    public static String getFileNoExt(String fileName) {
        // 检查文件名是否为空或null
        if (fileName == null || fileName.isEmpty()) {
            return CommonConstant.EMPTY;
        }
        // 查找文件名中最后一个"."的位置，代表扩展名的开始
        int index = fileName.lastIndexOf(".");
        // 如果找不到"."，说明文件名不包含扩展名，直接返回原文件名
        if (index == -1) {
            return fileName;
        }
        // 截取文件名中不包含扩展名的部分
        return fileName.substring(0, index);
    }

    /**
     * 获取文件内容（doc、docx、txt）
     *
     * @param file 文件
     * @return 文件内容
     * @throws IOException io异常
     */
    public static String getFileContent(File file) throws IOException {
        //获取文件类型
        String fileType = getFileType(file);
        switch (fileType) {
            case "txt":
                return getTxtContent(file);
            case "doc":
            case "docx":
                return readWordFile(file);
            default:
                throw new IllegalArgumentException("Unsupported file type: " + fileType);
        }
    }

    private static String getFileType(File file) {
        String fileName = file.getName().toLowerCase();
        if (fileName.endsWith(".txt")) {
            return "txt";
        } else if (fileName.endsWith(".docx")) {
            return "docx";
        } else if (fileName.endsWith(".doc")) {
            return "doc";
        } else {
            return "";
        }
    }

    /**
     * 获取txt文件的内容
     *
     * @param file 文件
     * @return txt文件的内容
     * @throws IOException IO异常
     */
    public static String getTxtContent(File file) throws IOException {
        return getTxtContent(Files.newInputStream(file.toPath()), "UTF-8");
    }

    /**
     * 获取docx文件内容
     *
     * @param file 文件
     * @return 内容
     * @throws IOException io异常
     */
    private static String readWordFile(File file) {
        String wordContent = null;
        wordContent = readDocFile(file.getPath());
        if (wordContent == null) {
            wordContent = readDocxFile(file.getPath());
        }
        return wordContent;
    }


//    public static void main(String[] args) {
//        String s = readDocxFile("D:\\home\\chnsys\\modules\\tester\\起诉状\\(2020)鲁0281民初1074号(起诉状).doc");
//        System.out.println(s);
//    }

    /**
     * 获取txt文件的内容
     *
     * @param file   文件
     * @param encode 编码方法
     * @return txt文件的内容
     * @throws IOException IO异常
     */
    public static String getTxtContent(File file, String encode) throws IOException {
        return getTxtContent(Files.newInputStream(file.toPath()), encode);
    }

    /**
     * 获取txt文件的内容
     *
     * @param inputStream 文件流
     * @return txt文件的内容
     * @throws IOException IO异常
     */
    public static String getTxtContent(InputStream inputStream) throws IOException {
        return getTxtContent(inputStream, "UTF-8");
    }

    /**
     * 获取txt文件的内容
     *
     * @param inputStream 文件流，用于读取txt文件内容
     * @param encode      编码格式，指定读取文件时使用的编码。如果未指定，则默认使用UTF-8编码
     * @return 返回txt文件的字符串内容，各行内容通过换行符连接
     * @throws IOException 如果在读取文件过程中发生IO异常，则抛出此异常
     */
    public static String getTxtContent(InputStream inputStream, String encode) throws IOException {
        // 创建一个列表用于存储读取的文件行
        List<String> buffer = new ArrayList<>();
        // 如果未指定编码格式，使用默认的UTF-8编码
        if (StrUtil.isEmpty(encode)) {
            encode = "UTF-8";
        }
        // 使用指定编码打开读取流
        InputStreamReader read = new InputStreamReader(inputStream, encode);
        // 创建缓冲edReader以优化读取性能
        BufferedReader bufferedReader = new BufferedReader(read);
        String readerStr;
        // 逐行读取文件内容并存储到buffer列表中
        while ((readerStr = bufferedReader.readLine()) != null) {
            buffer.add(readerStr);
        }
        // 关闭读取流
        read.close();
        // 将列表中的所有行连接成一个字符串，行之间使用换行符分隔
        return buffer.stream().map(String::valueOf).collect(Collectors.joining("\n"));
    }

    /**
     * 获取文件的编码格式
     *
     * @param filePath 文件路径
     * @return 文件的编码格式
     **/
    public static String getFileEncode(String filePath) throws IOException {
        return getFileEncode(new File(filePath));
    }

    /**
     * 获取文件的编码格式
     *
     * @param file 文件
     * @return 文件的编码格式
     **/
    public static String getFileEncode(File file) throws IOException {
        return getFileEncode(Files.newInputStream(file.toPath()));
    }

    /**
     * 获取文件的编码格式
     *
     * @param inputStream 文件流
     * @return 文件的编码格式
     **/
    public static String getFileEncode(InputStream inputStream) throws IOException {
        if (inputStream == null) {
            return null;
        }
        byte[] buf = new byte[4096];
        UniversalDetector detector = new UniversalDetector();

        int nRead;
        while ((nRead = inputStream.read(buf)) > 0 && !detector.isDone()) {
            detector.handleData(buf, 0, nRead);
        }
        detector.dataEnd();

        String encoding = detector.getDetectedCharset();
        detector.reset();
        if (Objects.isNull(encoding)) {
            throw new IllegalArgumentException("没有检测到编码");
        }
        return encoding;
    }

    /**
     * 将字节数组转换为十六进制字符串。
     *
     * @param src 输入的字节数组。
     * @return 对应的十六进制字符串。如果输入为null或空数组，则返回null。
     */
    private static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        // 检查输入是否为空，为空则直接返回null
        if (src == null || src.length == 0) {
            return null;
        }
        // 遍历字节数组，将每个字节转换为十六进制字符串
        for (byte b : src) {
            int v = b & 0xFF; // 将字节转换为无符号整数
            String hv = Integer.toHexString(v); // 转换为十六进制字符串
            // 如果生成的十六进制字符串长度小于2，前面补0
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    /**
     * 根据模值获取文件类型
     *
     * @param magicNumberCode 模值代码
     * @return 对应的类型枚举
     */
    public static FileTypeHexEnum getByMagicNumberCode(String magicNumberCode) {
        if (StringUtils.isNotBlank(magicNumberCode)) {
            for (FileTypeHexEnum typeHexEnum : FileTypeHexEnum.values()) {
                if (magicNumberCode.toUpperCase().startsWith(typeHexEnum.getMagicNumberCode())) {
                    return typeHexEnum;
                }
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("文件格式匹配失败！模值为：" + magicNumberCode);
        }
        return FileTypeHexEnum.DOCX;
    }

    /**
     * 从指定文件中读取JSON内容，并将其转换为指定类型的Java对象。
     *
     * @param file  指定的文件，需要是一个可读的文件。
     * @param clazz 需要转换成的Java对象的类型。
     * @return 从JSON字符串转换后的对象实例，如果文件不可读、不存在或内容为空，则返回null。
     * @throws IOException 如果在读取文件内容时发生错误。
     */
    public static <T> T getJson(File file, Class<T> clazz) throws IOException {
        // 检查文件是否有效
        if (file == null || !file.isFile() || !file.canRead()) {
            return null;
        }
        // 读取文件中的文本内容
        String json = getTxtContent(file);
        // 如果读取的内容为空，则返回null；否则将JSON字符串转换为指定类型的对象
        return StringUtils.isBlank(json) ? null : JsonUtils.toBean(json, clazz);
    }

    /**
     * 复制文件至指定路径
     *
     * @param srcPath 源文件地址
     * @param dirPath 目的路径
     * @param isDel   是否删除源文件
     * @return 目的文件
     */
    public static File copyFileToDir(String srcPath, String dirPath, String fileName, boolean isDel) {
        FileUtil.mkdir(dirPath);
        File curPath = FileUtils.copy(srcPath, dirPath, true);
        if (isDel) {
            FileUtils.del(srcPath);
        }
        return new File(curPath, fileName);
    }

    /**
     * 修改文件名称
     *
     * @param targetName 标准名
     * @param file       目的文件
     */
    public static void renameFile(File file, String targetName) {
        if (file == null) {
            if (log.isErrorEnabled()) {
                log.error("文件重命名失败，文件为空");
            }
            return;
        }
        FileUtils.rename(file, targetName, true, true);
    }


    /**
     * 将指定源文件复制到目标目录，并重命名为新的文件名。
     *
     * @param srcDirPath  源文件所在目录的路径。
     * @param destDirPath 目标目录的路径。
     * @param srcName     源文件名。
     * @param destName    新的文件名。
     */
    public static void renameAndCopy(String srcDirPath, String destDirPath, String srcName, String destName) {
        // 复制源文件到目标目录
        File targetFile = copyFileToDir(srcDirPath, destDirPath, srcName, false);
        // 重命名已复制的文件
        renameFile(targetFile, destName);
    }


    /**
     * 根据文件路径获取文件模值
     *
     * @param filePath 文件路径
     * @return 对应的模值
     */
    public static String getFileHeadHexByPath(String filePath) {
        byte[] headBytes = new byte[28];
        try (InputStream inputStream = Files.newInputStream(Paths.get(filePath))) {
            int readLength = inputStream.read(headBytes, 0, 28);
            if (log.isInfoEnabled()) {
                log.info("---> {}读取的长度是{} <---", filePath, readLength);
            }
        } catch (IOException e) {
            log.error("---> 读取文件模值异常，文件是{} <---", filePath);
        }
        return bytesToHexString(headBytes);
    }

    /**
     * 流转换成文件
     * input stream -> file
     *
     * @param inputStream 文件流
     */
    public static void inputStreamToFile(InputStream inputStream, String filename) {
        try {
            //新建文件
            File file = new File(filename);
            if (!file.getParentFile().exists()) {
                //如果不存在
                FileUtil.mkdir(file.getParentFile());
            }
            OutputStream os = Files.newOutputStream(file.toPath());
            int read;
            byte[] bytes = new byte[1024 * 1024];
            //先读后写
            while ((read = inputStream.read(bytes)) > 0) {
                byte[] wBytes = new byte[read];
                System.arraycopy(bytes, 0, wBytes, 0, read);
                os.write(wBytes);
            }
            os.flush();
            os.close();
            inputStream.close();
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("文件流转换成为文件失败");
            }
        }
    }

    /**
     * url -> input stream
     * 通过url得到对应的文件流
     *
     * @param fileUrl 文件地址
     * @return 文件流
     */
    public static InputStream getInputStreamFromUrl(String fileUrl) {
        try {
            URL url = new URL(fileUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                return connection.getInputStream();
            } else {
                return null;
            }
        } catch (IOException e) {
            if (log.isErrorEnabled()) {
                log.error("--> 抓取文件地址或者url转换文件流失败 <--");
            }
            return null;
        }
    }

    /**
     * 将InputStream中的数据写入到指定的File中。
     *
     * @param ins  输入流，数据的来源。
     * @param file 输出文件，数据的去向。
     * @throws IOException 在读写过程中发生I/O错误时抛出该异常。
     */
    public static void inputStreamToFile(InputStream ins, File file) throws IOException {
        // 创建一个输出流，用于将数据写入文件
        OutputStream os = Files.newOutputStream(file.toPath());
        int bytesRead;
        byte[] buffer = new byte[8192]; // 创建一个缓冲区，用于存储从输入流读取的数据

        // 循环读取输入流直到没有更多的数据
        while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
            // 将读取的数据写入到输出文件中
            os.write(buffer, 0, bytesRead);
        }

        // 关闭输出流
        os.close();
        // 关闭输入流
        ins.close();
    }


    /**
     * 上传文件到指定路径。
     *
     * @param file         需要上传的文件，类型为MultipartFile。
     * @param templateName 文件模板名称，上传后文件将以此命名。
     * @param targetPath   上传文件的目标路径。
     *                     注：此方法不会返回任何值，也不会抛出异常，错误处理通过日志输出。
     */
    public static void uploadFile(MultipartFile file, String templateName, String targetPath) {
        // 格式化文件名，去除可能存在的非法字符
        templateName = doFormatForName(templateName);
        // 创建目标路径的文件夹
        boolean b = FileUtils.mkDir(targetPath);
        if (b) {
            try {
                // 构建目标文件的完整路径
                File target = new File(targetPath + File.separator + templateName);
                // 将上传的文件转存到目标文件
                file.transferTo(target);
            } catch (IOException e) {
                // 文件转存异常处理，打印堆栈信息
                e.printStackTrace();
            }
        }
    }

    /**
     * 将文件转换为字节数组。
     *
     * @param file 需要转换的文件对象。
     * @return 文件的字节数组。如果转换失败，则返回null。
     */
    public static byte[] getBytesByFiles(File file) {
        try (FileInputStream fileInputStream = new FileInputStream(file);
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[4096]; // 用于读取文件的缓冲区
            int bytesRead;

            // 循环读取文件直到末尾，并将读取的内容写入字节数组输出流
            while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }
            // 返回字节数组输出流的内容，即文件的字节数组表示
            return byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            log.error("file -> byte[]转换失败！！！"); // 记录转换失败的错误日志
            return null;
        }
    }

    /**
     * 构建文件存储路径。
     *
     * @param basePath     基础路径，文件存储的根目录。
     * @param key          用于分类的键值，可以根据不同的key将文件存储在不同的子目录中。
     * @param templateName 模板文件名，作为生成文件名的一部分。
     * @param timeFlag     是否在路径中添加时间戳，用于区分不同时间创建的文件。
     * @param numberFlag   是否在路径中添加随机数，用于进一步区分同一时间创建的文件。
     * @return 完整的文件存储路径，包括目录和文件名。
     */
    public static String pathBuild(String basePath, String key, String templateName, boolean timeFlag, boolean numberFlag) {
        String timePath = CommonConstant.EMPTY;
        // 根据timeFlag决定是否添加时间路径
        if (timeFlag) {
            // 生成时间路径，包含年月日时分秒
            timePath = DateConstant.df.get().format(DateUtils.getNowDate());
        }
        // 根据numberFlag决定是否添加随机数
        if (numberFlag) {
            // 生成4位随机数，用以区分同一时间生成的文件
            int randomNumber = (int) (Math.random() * (9999 - 1000 + 1) + 1000);
            if (StringUtils.isEmpty(timePath)) {
                timePath = String.valueOf(randomNumber);
            } else {
                timePath = timePath + "-" + randomNumber;
            }
        }

        // 根据key和timePath构建输出目录
        String outFolder;
        if (StringUtils.isEmpty(timePath)) {
            if (StringUtils.isBlank(key)) {
                outFolder = basePath;
            } else {
                outFolder = basePath + File.separator + key;
            }
        } else {
            if (StringUtils.isBlank(key)) {
                outFolder = basePath + File.separator + timePath;
            } else {
                outFolder = basePath + File.separator + key + File.separator + timePath;
            }
        }

        File holder = new File(outFolder);

        // 检查目录是否存在，不存在则尝试创建
        if (!holder.exists()) {
            boolean mkdirsFlag = holder.mkdirs();
            if (!mkdirsFlag) {
                log.error("---> 创建文件夹失败 <---");
            }
        }
        // 返回完整的文件路径
        return outFolder + File.separator + templateName;
    }


    /**
     * 对给定的模板名称进行格式化处理。
     * 主要替换空格以及特定的括号字符，以确保模板名称的标准化。
     *
     * @param templateName 待格式化的模板名称。
     * @return 格式化后的模板名称。如果输入为空或仅包含空白字符，将返回空字符串。
     */
    private static String doFormatForName(String templateName) {
        // 如果模板名称不为空，则进行格式化处理
        if (StringUtils.isNotEmpty(templateName)) {
            // 删除所有空格
            templateName = templateName.replace(" ", "");
            // 将中文括号替换为英文括号
            templateName = templateName.replace("）", ")");
            templateName = templateName.replace("（", "(");
        }
        return templateName;
    }

    /**
     * 下载文件。
     *
     * @param filePath 文件所在的路径。
     * @param fileName 需要下载的文件名。
     * @return 如果文件存在，则返回文件对象；否则返回null。
     */
    public File downloadFile(String filePath, String fileName) {

        // 构建文件路径
        File file = new File(filePath + File.separator + fileName);

        // 检查文件是否存在
        if (!file.exists()) {
            // 如果日志记录级别允许，输出文件不存在的信息
            if (log.isInfoEnabled()) {
                log.info("---> 要下载的文件不存在 <---");
            }
            return null;
        }

        return file;
    }
}